#include "littlevgl2rtt.h" 
#include "lvgl.h" 

#include <rtthread.h> 
#include <rtdevice.h>  

#include "libbmp.h"
#include "fcntl.h"
#include "stdio.h"
#include "unistd.h"
#include "dirent.h"

struct point_object
{
    lv_obj_t *lv_text;
    char str[2];
    uint16_t value;
};
static const char *point_max_str[] = 
{
    /*     * 
     * +: ***
     *     * 
    */
    "+"
};
static const char *point_str[] = 
{
    /*      *      * *     *  
     * *:  ***  x:  *  o: * * -: ***
     *     * *     * *     *  
    */
    "*","x","o","-"
};
#define POINT_TYPE_MASK         0x8000
#define POINT_TYPE_3            0x0000
#define POINT_TYPE_5            0x8000
static const uint16_t point_max_value[] =
{
    0b0000000010111010,//"+"
};
static const uint16_t point_value[] =
{
    0b0000000010111101,//"*"
    0b0000000101010101,//"x"
    0b0000000010101010,//"o"
    0b0000000000111000,//"-"
};
static rt_device_t lcd_dev = NULL;
static struct rt_device_graphic_info lcd_info = {0};
static lv_style_t style_default;
static struct rt_i2c_bus_device *i2c_bus = NULL;
static lv_obj_t *table_view = NULL;
static lv_obj_t *iray_img = NULL;
static struct point_object max_point = {NULL};
static struct point_object temp_point[4] = {NULL};
static lv_obj_t *iray_color_bar_img = NULL;
static lv_obj_t *iray_color_bar_max_value = NULL;
static lv_obj_t *iray_color_bar_min_value = NULL;
static lv_obj_t *iray_btn_pause = NULL;
static lv_obj_t *iray_btn_pause_label = NULL;
static lv_obj_t *iray_btn_save = NULL;
static lv_obj_t *iray_btn_save_label = NULL;
static lv_obj_t *iray_btn_measure = NULL;
static lv_obj_t *iray_btn_measure_label = NULL;
static lv_obj_t *mesg_label = NULL;
static lv_obj_t *file_list_page = NULL;
static lv_obj_t *file_list = NULL;
static lv_obj_t *view_img = NULL;
#define LV_IMG_WIDTH            240//160
#define LV_IMG_HEIGHT           180//120
#define LV_COLOR_BAR_WIDTH      LV_IMG_WIDTH
#define LV_COLOR_BAR_HEIGHT     16
#define LV_MESG_LABEL_WIDTH     LV_IMG_WIDTH
#define LV_MESG_LABEL_HEIGHT    32
#define LV_BUTTON_WIDTH         80
#define LV_BUTTON_HEIGHT        30
#define LV_VIEW_WIDTH           LV_IMG_WIDTH
#define LV_VIEW_HEIGHT          (LV_IMG_HEIGHT + LV_COLOR_BAR_HEIGHT + LV_MESG_LABEL_HEIGHT)
#define LV_FILE_LIST_WIDTH      LV_VIEW_WIDTH
#define LV_FILE_LIST_HEIGHT     (360 - LV_VIEW_HEIGHT) /* LCD_H - LV_IMG_HEIGHT = 180 */
//#define LV_SNAPSHOT_HEIGHT      LV_VIEW_HEIGHT
static lv_color_t img_buf[LV_IMG_WIDTH*LV_IMG_HEIGHT]={0};
static lv_img_dsc_t img_dsc =
{
    .header = 
    {
        .always_zero = 0,
        .w = LV_IMG_WIDTH,
        .h = LV_IMG_HEIGHT,
        .cf = LV_IMG_CF_TRUE_COLOR,
    },
    .data_size = sizeof(img_buf),
    .data = (void *)img_buf,
};
static lv_color_t color_bar_buf[LV_COLOR_BAR_WIDTH*LV_COLOR_BAR_HEIGHT]={0};
static lv_img_dsc_t color_bar_dsc =
{
    .header = 
    {
        .always_zero = 0,
        .w = LV_COLOR_BAR_WIDTH,
        .h = LV_COLOR_BAR_HEIGHT,
        .cf = LV_IMG_CF_TRUE_COLOR,
    },
    .data_size = sizeof(color_bar_buf),
    .data = (void *)color_bar_buf,
};
static lv_color_t view_buf[LV_VIEW_WIDTH*LV_VIEW_HEIGHT]={0};
static lv_img_dsc_t view_dsc =
{
    .header = 
    {
        .always_zero = 0,
        .w = LV_VIEW_WIDTH,
        .h = LV_VIEW_HEIGHT,
        .cf = LV_IMG_CF_TRUE_COLOR,
    },
    .data_size = sizeof(view_buf),
    .data = (void *)view_buf,
};
void updateMaxPoint(int max_index, int w)
{
    if(max_point.lv_text)
    {
        int x = max_index%w;
        int y = max_index/w;
        //max_point.index = y * LV_IMG_WIDTH + x;
        //max_point.v = (int)mlx90640Bound.max;
        lv_obj_set_pos(max_point.lv_text, x - 8, y - 8);
    }
}
void updateMesg(float *temps, struct point_object *points, int num, bool append)
{
    char buff[64];
    int off = 0;
    char *old = lv_label_get_text(mesg_label);
    if(append)
    {
        off = rt_sprintf(buff, "%s", old);
    }
    for(int i=0; i<num; i++)
    {
        if(lv_obj_get_hidden(points[i].lv_text))
        {
            continue;
        }
        int x = lv_obj_get_x(points[i].lv_text) + 8;
        int y = lv_obj_get_y(points[i].lv_text) + 8;
        int index = (y * LV_IMG_WIDTH) + x;
        int temp_i = (int)(temps[index]*10);
        off += rt_sprintf(&buff[off], "%s:%d.%d;", points[i].str, temp_i/10, abs(temp_i%10));
    }
    buff[off] = '\0';
    lv_label_set_text(mesg_label, buff);
}
void updateMaxPointMesg(float *max, bool append)
{
    updateMesg(max, &max_point, 1, append);
}
void updatePointsMesg(float *temps, bool append)
{
    updateMesg(temps, temp_point, sizeof(temp_point)/sizeof(temp_point[0]), append);
}
void color2pixels(lv_color_t *color_buf, bmp_pixel *pixels, int w, int h);
void pixels2color(bmp_pixel *pixels, lv_color_t *color_buf, int w, int h);
void appendPoint(bmp_pixel *pixels, struct point_object *points, int num);
static inline const char *_event2str(lv_event_t e)
{
    switch(e)
    {
        case LV_EVENT_PRESSED:
        return "PRESSED";
        case LV_EVENT_RELEASED:
        return "RELEASED";
        case LV_EVENT_PRESSING:
        return "PRESSING";
        case LV_EVENT_PRESS_LOST:
        return "PRESS_LOST";
        case LV_EVENT_FOCUSED:
        return "FOCUSED";
        case LV_EVENT_DEFOCUSED:
        return "DEFOCUSED";
        case LV_EVENT_LONG_PRESSED:
        return "LONG_PRESSED";
        case LV_EVENT_LONG_PRESSED_REPEAT:
        return "LONG_PRESSED_REPEAT";
        case LV_EVENT_DRAG_BEGIN:
        return "DRAG_BEGIN";
        case LV_EVENT_DRAG_END:
        return "DRAG_END";
        case LV_EVENT_SHORT_CLICKED:
        return "SHORT_CLICKED";
        case LV_EVENT_CLICKED:
        return "CLICKED";
    }
    return NULL;
}
void grey_boundary_param_print(void);
static void update_file_list(lv_obj_t *file_list);
static void btn_event_cb(lv_obj_t *btn, lv_event_t e)
{
    if(btn)
    {
        lv_obj_t *label = lv_obj_get_child(btn, NULL);
        //rt_kprintf("btn:%s(%p), %d\r\n", lv_label_get_text(label), btn, e);
        if(e == LV_EVENT_CLICKED)
        {
            rt_kprintf("btn:%s(%p), %s\r\n", lv_label_get_text(label), btn, "CLICKED");
        }
        else if(e == LV_EVENT_RELEASED)
        {
            rt_kprintf("btn:%s(%p), %s\r\n", lv_label_get_text(label), btn, "RELEASED");
        }
        if((btn == iray_btn_pause) && (e == LV_EVENT_RELEASED))
        {
            if(rt_strcmp(lv_label_get_text(label), "PAUSE") == 0)
            {
                lv_label_set_text(label, "START");
                grey_boundary_param_print();
            }
            else
            {
                lv_label_set_text(label, "PAUSE");
            }
        }
        else if((btn == iray_btn_save) && (e == LV_EVENT_RELEASED))
        {
            char fname[32];
            rt_sprintf(fname, "%d.bmp", rt_tick_get());
            int fd = open(fname, O_WRONLY | O_CREAT);
            if(fd >= 0)
            {
                bmp_img img;
                bmp_img_init_df(&img, LV_VIEW_WIDTH, LV_VIEW_HEIGHT);
                if(img.img_pixels)
                {
                    #if 1
                    color2pixels((lv_color_t *)lcd_info.framebuffer, img.img_pixels, LV_VIEW_WIDTH, LV_VIEW_HEIGHT);
                    #else
                    color2pixels(img_buf, img.img_pixels, LV_IMG_WIDTH, LV_IMG_HEIGHT);
                    appendPoint(img.img_pixels, temp_point, sizeof(temp_point)/sizeof(temp_point[0]));
                    appendPoint(img.img_pixels, &max_point, 1);
                    color2pixels(color_bar_buf, &img.img_pixels[LV_IMG_WIDTH*LV_IMG_HEIGHT], LV_COLOR_BAR_WIDTH, LV_COLOR_BAR_HEIGHT);
                    #endif
                    bmp_img_write(&img, (void *)fd);
                }
                else
                {
                    rt_kprintf("pixels is NULL\r\n");
                }
                bmp_img_free(&img);
                close(fd);
                update_file_list(file_list);
            }
        }
        else if((btn == iray_btn_measure) && (e == LV_EVENT_RELEASED))
        {
            if(rt_strcmp(lv_label_get_text(label), "MEAS") == 0)
            {
                lv_obj_set_hidden(temp_point[1].lv_text, false);
                lv_obj_set_click(temp_point[1].lv_text, true);
                lv_obj_set_drag(temp_point[1].lv_text, true);
            }
            else
            {
                lv_obj_set_hidden(temp_point[1].lv_text, true);
                lv_obj_set_click(temp_point[1].lv_text, false);
                //lv_label_set_text(label, "MEAS");
                lv_obj_set_drag(temp_point[1].lv_text, false);
            }
        }
    }
}
static void point_event_cb(lv_obj_t *point, lv_event_t e)
{
    if(point)
    {
        lv_indev_t *indev = lv_indev_get_act();
        lv_point_t p;
        const char *evt_str = _event2str(e);
        lv_indev_get_vect(indev, &p);
        if(evt_str)
        {
            rt_kprintf("point:%s(%p), %s, %p(%d,%d)\r\n", lv_label_get_text(point), point, evt_str, indev, p.x, p.y);
        }
        else
        {
            rt_kprintf("point:%s(%p), %d, %p(%d,%d)\r\n", lv_label_get_text(point), point, e, indev, p.x, p.y);
        }
        if(e == LV_EVENT_PRESSING)
        {
            int x = lv_obj_get_x(point) + p.x;
            int y = lv_obj_get_y(point) + p.y;
            x = (x >= LV_IMG_WIDTH)?(LV_IMG_WIDTH - 1):(x < 0)?0:x;
            y = (y >= LV_IMG_HEIGHT)?(LV_IMG_HEIGHT - 1):(y < 0)?0:y;
            lv_obj_set_pos(point, x, y);
        }
    }
}
static void file_list_event_cb(lv_obj_t *file_list, lv_event_t e)
{
    if(file_list)
    {
        const char *evt_str = _event2str(e);
        if(e == LV_EVENT_PRESSED)
        {
            uint16_t row;
            uint16_t col;
            lv_res_t r = lv_table_get_pressed_cell(file_list, &row, &col);
            if(r == LV_RES_OK) {  /*Skip the first row and col*/
                const char *symbol = lv_table_get_cell_value(file_list, row, 0);
                if(rt_strcmp(symbol, LV_SYMBOL_IMAGE) == 0) {
                    for(uint16_t i = 0; i < lv_table_get_row_cnt(file_list); i++)
                    {
                        lv_table_set_cell_value(file_list, i, 0, LV_SYMBOL_IMAGE);
                    }
                    lv_table_set_cell_value(file_list, row, 0, LV_SYMBOL_OK);
                    const char * fname = lv_table_get_cell_value(file_list, row, 1);
                    int fd;
                    #if 0
                    char path_buff[64];
                    fd = rt_sprintf(path_buff, "/%s", fname);
                    path_buff[fd] = '\0';
                    fd = open(path_buff, O_RDONLY);
                    #else
                    fd = open(fname, O_RDONLY);
                    #endif
                    lseek(fd, 0, SEEK_SET);
                    if(fd >= 0)
                    {
                        bmp_img img = {0};
                        bmp_header *header = &img.img_header;
                        //bmp_img_init_df(&img, LV_VIEW_WIDTH, LV_VIEW_HEIGHT);
                        //if(img.img_pixels)
                        {
                            //lv_label_set_text(iray_btn_pause_label, "START");
                            bmp_img_read(&img, (void *)fd);
                            if((header->biWidth == LV_VIEW_WIDTH) && (header->biHeight == LV_VIEW_HEIGHT))
                            {
                                pixels2color(img.img_pixels, view_buf, LV_VIEW_WIDTH, LV_VIEW_HEIGHT);
                            }
                            else
                            {
                                rt_kprintf("img:%s, %dx%d != %dx%d\r\n", fname, header->biWidth, header->biHeight, LV_VIEW_WIDTH, LV_VIEW_HEIGHT);
                            }
                        }
                        bmp_img_free(&img);
                        close(fd);
                    }
                } else {
                    lv_table_set_cell_value(file_list, row, 0, LV_SYMBOL_IMAGE);
                    //lv_label_set_text(iray_btn_pause_label, "PAUSE");
                }
            }
            //rt_kprintf("file list:%p, %s, (%d, %d)\r\n", file_list, evt_str, row, col);
        }
        else if(evt_str == NULL)
        {
            rt_kprintf("file list:%p, %d\r\n", file_list, e);
        }
    }
}
#if 0
static void point_event_cb(lv_obj_t *point, lv_event_t e)
{
    if(point)
    {
        if(point == temp_point[1].lv_text)
        {
            lv_area_t coord;
            lv_coord_t x = lv_obj_get_x(temp_point[1].lv_text);
            lv_coord_t y = lv_obj_get_y(temp_point[1].lv_text);
            temp_point[1].index = y * LV_IMG_WIDTH + x;
        }
    }
}
#endif
static void update_file_list(lv_obj_t *file_list)
{
    if(file_list == NULL)
    {
        return ;
    }
    int index = 0;
    char fname[64];
    struct dirent *dir_info;
    DIR *dir = opendir("/");
    if(dir == NULL)
    {
        rt_kprintf("open dir failed!\r\n");
        return ;
    }
    while(1)
    {
        dir_info = readdir(dir);
        if(dir_info == NULL)
        {
            break;
        }
        if(dir_info->d_type == DT_REG)
        {
            rt_memcpy(fname, dir_info->d_name, dir_info->d_namlen);
            fname[dir_info->d_namlen] = '\0';
            if(rt_strstr(fname, ".bmp") != NULL)
            {
                lv_table_set_cell_value(file_list, index, 0, LV_SYMBOL_IMAGE);
                lv_table_set_cell_value(file_list, index, 1, fname);
                index ++;
            }
        }
    }
    closedir(dir);
    lv_table_set_row_cnt(file_list, index);
    rt_kprintf("row_cnt:%d, index:%d\r\n", lv_table_get_row_cnt(file_list), index);
}
static void points_init(lv_obj_t *parent, struct point_object *points, const char *str[], const uint16_t *value, int num)
{
    for(int i=0; i<num; i++)
    {
        points[i].lv_text = lv_label_create(parent, NULL);
        lv_label_set_text(points[i].lv_text, str[i]);
        lv_obj_set_size(points[i].lv_text, 16, 16);
        lv_obj_set_pos(points[i].lv_text, LV_IMG_WIDTH/2, LV_IMG_HEIGHT/2);
        lv_label_set_align(points[i].lv_text, LV_LABEL_ALIGN_CENTER);
        //lv_obj_align(points[i].lv_text, parent, LV_ALIGN_CENTER, 0, 0);
        lv_obj_set_event_cb(points[i].lv_text, point_event_cb);
        lv_obj_set_hidden(points[i].lv_text, true);
        //points[i].index = (LV_IMG_HEIGHT + 1) * LV_IMG_WIDTH / 2;
        points[i].str[0] = str[i][0];
        points[i].str[1] = '\0';
        points[i].value = value[i];
    }
}
static void lvgl_obj_init(void)
{
    lv_style_init(&style_default);
    lv_disp_size_t ds = lv_disp_get_size_category(NULL);
    // table view
    table_view = lv_tabview_create(lv_scr_act(), NULL);
    lv_obj_set_pos(table_view, 0, 0);
    //lv_obj_set_size(table_view, lcd_info.width, lcd_info.height);
    lv_tabview_set_btns_pos(table_view, LV_TABVIEW_TAB_POS_NONE);
    
    lv_obj_t *cam = lv_tabview_add_tab(table_view, "cam");
    lv_obj_t *view = lv_tabview_add_tab(table_view, "view");
    lv_obj_set_size(cam, lcd_info.width, lcd_info.height);
    lv_obj_set_size(view, lcd_info.width, lcd_info.height);
    //lv_page_set_scroll_propagation(cam, false);
    //lv_page_set_scroll_propagation(view, false);
    lv_page_set_scrollable_fit(cam, LV_FIT_NONE);
    lv_page_set_scrollable_fit(view, LV_FIT_NONE);
    lv_page_set_scrl_width(cam, lv_obj_get_width(cam));
    lv_page_set_scrl_height(cam, lv_obj_get_height(cam));
    lv_page_set_scrl_width(view, lv_obj_get_width(view));
    lv_page_set_scrl_height(view, lv_obj_get_height(view));
    // img
    iray_img = lv_img_create(cam, NULL);
    lv_obj_set_size(iray_img, LV_IMG_WIDTH, LV_IMG_HEIGHT);
    //lv_obj_set_width_margin(iray_img, ds);
    //lv_obj_set_height_margin(iray_img, ds);
    // color bar
    iray_color_bar_img = lv_img_create(cam, NULL);
    //lv_obj_set_pos(iray_color_bar_img, 0, LV_IMG_HEIGHT);
    lv_obj_set_size(iray_color_bar_img, LV_COLOR_BAR_WIDTH, LV_COLOR_BAR_HEIGHT);
    //lv_obj_set_width_margin(iray_color_bar_img, ds);
    lv_obj_align(iray_color_bar_img, iray_img, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
    // max
    iray_color_bar_max_value = lv_label_create(cam, NULL);
    //lv_obj_set_pos(iray_color_bar_max_value, LV_COLOR_BAR_WIDTH, 0);
    //lv_label_set_align(iray_color_bar_max_value, LV_LABEL_ALIGN_RIGHT);
    lv_obj_align(iray_color_bar_max_value, iray_color_bar_img, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
    // min
    iray_color_bar_min_value = lv_label_create(cam, NULL);
    //lv_obj_set_pos(iray_color_bar_min_value, 0, 0);
    lv_obj_align(iray_color_bar_min_value, iray_color_bar_img, LV_ALIGN_IN_TOP_LEFT, 0, 0);
    //mesg
    mesg_label = lv_label_create(cam, NULL);
    lv_obj_align(mesg_label, iray_color_bar_img, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
    lv_obj_set_size(mesg_label, LV_MESG_LABEL_WIDTH, LV_MESG_LABEL_HEIGHT);
    //lv_obj_set_width_margin(mesg_label, ds);
    lv_label_set_text(mesg_label, " ");
    // point
    points_init(iray_img, temp_point, point_str, point_value, sizeof(temp_point)/sizeof(temp_point[0]));
    points_init(iray_img, &max_point, point_max_str, point_max_value, 1);
    lv_obj_set_hidden(max_point.lv_text, false);
    // btn pause
    iray_btn_pause = lv_btn_create(cam, NULL);
    lv_obj_set_size(iray_btn_pause, LV_BUTTON_WIDTH, LV_BUTTON_HEIGHT);
    lv_obj_align(iray_btn_pause, cam, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
    lv_obj_set_event_cb(iray_btn_pause, btn_event_cb);
    iray_btn_pause_label = lv_label_create(iray_btn_pause, NULL);
    lv_label_set_text(iray_btn_pause_label, "PAUSE");
    // btn save
    iray_btn_save = lv_btn_create(cam, NULL);
    lv_obj_set_size(iray_btn_save, LV_BUTTON_WIDTH, LV_BUTTON_HEIGHT);
    lv_obj_align(iray_btn_save, cam, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
    lv_obj_set_event_cb(iray_btn_save, btn_event_cb);
    iray_btn_save_label = lv_label_create(iray_btn_save, NULL);
    lv_label_set_text(iray_btn_save_label, "SAVE");
    // btn add
    iray_btn_measure = lv_btn_create(cam, NULL);
    lv_obj_set_size(iray_btn_measure, LV_BUTTON_WIDTH, LV_BUTTON_HEIGHT);
    lv_obj_align(iray_btn_measure, cam, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
    lv_obj_set_event_cb(iray_btn_measure, btn_event_cb);
    iray_btn_measure_label = lv_label_create(iray_btn_measure, NULL);
    lv_label_set_text(iray_btn_measure_label, "MEAS");
    // view
    view_img = lv_img_create(view, NULL);
    lv_obj_align(view_img, view, LV_ALIGN_IN_TOP_LEFT, 0, 0);
    lv_obj_set_size(view_img, LV_VIEW_WIDTH, LV_VIEW_HEIGHT);
    // file list page
    file_list_page = lv_page_create(view, NULL);
    lv_obj_align(file_list_page, view_img, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
    //lv_page_set_scrollable_fit2(file_list_page, LV_FIT_MAX, LV_FIT_MAX);
    int list_page_h = lv_obj_get_height(view) - lv_obj_get_height(view_img);
    lv_obj_set_size(file_list_page, LV_FILE_LIST_WIDTH, list_page_h);
    lv_page_set_scrollable_fit(file_list_page, LV_FIT_MAX);
    //lv_page_set_scrl_width(file_list_page, LV_FILE_LIST_WIDTH);
    //lv_page_set_scrl_height(file_list_page, list_page_h);
    rt_kprintf("list_page:%dx%d,ds:%d\r\n", lv_page_get_width_fit(file_list_page), lv_page_get_height_fit(file_list_page), ds);
    //lv_page_set_scroll_propagation(file_list_page, true);
    // file list
    file_list = lv_table_create(file_list_page, NULL);
    //lv_obj_align(file_list, file_list_page, LV_ALIGN_CENTER, 0, 0);
    //lv_obj_set_size(file_list_page, table_w_max, table_h_max);
    //lv_obj_get_width_fit(file_list);
    lv_obj_set_width(file_list, lv_page_get_width_fit(file_list_page));
    lv_obj_set_event_cb(file_list, file_list_event_cb);
    lv_obj_set_drag_parent(file_list, true);
    lv_table_set_col_cnt(file_list, 2);
    lv_table_set_row_cnt(file_list, 1);
    lv_table_set_col_width(file_list, 0, 30);
    lv_table_set_col_width(file_list, 1, lv_page_get_width_fit(file_list_page) - 30);
    update_file_list(file_list);
    //lv_obj_set_hidden(file_list, true);
}
extern rt_uint32_t str2hex(const char *str);
int readTempValues(void);
void generatePicture(void);
void updatePointsAndMesg(void);
void tempSensorInit(void);
static void iray_camera_entry(void *arg)
{
    rt_tick_t tick = rt_tick_get();
    while(1)
    {
        if(table_view)
        {
            if(lv_tabview_get_tab_act(table_view) == 0)
            {
                if(iray_btn_pause_label && rt_strcmp(lv_label_get_text(iray_btn_pause_label), "PAUSE") == 0)
                {
                    if(readTempValues() == 0)
                    {
                        generatePicture();
                        lv_img_set_src(iray_img, &img_dsc); //draw img
                        lv_img_set_src(iray_color_bar_img, &color_bar_dsc); //draw color bar
                    }
                }
                updatePointsAndMesg();
            }
            else
            {
                lv_img_set_src(view_img, &view_dsc);
            }
        }
        
        rt_thread_delay_until(&tick, rt_tick_from_millisecond(100));
    }
}
void iray_camera_init(const char *bus)
{
    i2c_bus = (void *)rt_device_find(bus);
    if(i2c_bus == NULL)
    {
        rt_kprintf("bus %s not found.\r\n", bus);
        return ;
    }
    if(RT_EOK != rt_device_open((void *)i2c_bus, RT_DEVICE_FLAG_RDWR))
    {
        rt_kprintf("fail to open bus %s.\r\n", bus);
        return ;
    }
    if(iray_img == NULL)
    {
        /* init littlevGL */
        lcd_dev = rt_device_find("lcd");
        if(lcd_dev == NULL)
        {
            return ;
        }
        if(littlevgl2rtt_init("lcd") != RT_EOK)
        {
            lcd_dev = NULL;
            return ; 
        }
        rt_device_control(lcd_dev, RTGRAPHIC_CTRL_GET_INFO, &lcd_info);
        rt_kprintf("lcd:%dx%d, %p\r\n", lcd_info.width, lcd_info.height, lcd_info.framebuffer);
        lvgl_obj_init();
    }
    tempSensorInit();
    rt_thread_t tid = rt_thread_create("iray", iray_camera_entry, NULL, 8192, 5, 10);
    if(tid)
    {
        rt_thread_startup(tid);
    }
    else
    {
        rt_kprintf("create iray failed!\r\n");
    }
}
static int rt_lvgl_iray_camera_cmd(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;
    rt_thread_t thread = RT_NULL;

    if(iray_img == NULL)
    {
        /* init littlevGL */
        lcd_dev = rt_device_find("lcd");
        if(lcd_dev == NULL)
        {
            return -RT_ERROR;
        }
        ret = littlevgl2rtt_init("lcd");
        if(ret != RT_EOK)
        {
            lcd_dev = NULL;
            return ret; 
        }
        rt_device_control(lcd_dev, RTGRAPHIC_CTRL_GET_INFO, &lcd_info);
        rt_kprintf("lcd:%dx%d, %p\r\n", lcd_info.width, lcd_info.height, lcd_info.framebuffer);
        lvgl_obj_init();
    }
    if(rt_strcmp(argv[1], "set") == 0)
    {
        if(argc > 2)
        {
            uint32_t color_hex = str2hex(argv[2]);
            lv_color_t color = lv_color_hex(color_hex);
            for(int i=0; i<sizeof(img_buf)/sizeof(img_buf[0]); i++)
            {
                img_buf[i] = color;
            }
            lv_img_set_src(iray_img, &img_dsc);
        }
        else
        {
            rt_kprintf("<> set <color>\r\n");
        }
    }
    else if(rt_strcmp(argv[1], "config") == 0)
    {
        if(argc > 3)
        {
            if(rt_strcmp(argv[2], "label_max") == 0)
            {
                uint32_t align = str2hex(argv[3]);
                lv_obj_align(iray_color_bar_max_value, iray_color_bar_img, align, 0, 0);
            }
        }
        else
        {
            rt_kprintf("<> set <color>\r\n");
        }
    }
    else if(rt_strcmp(argv[1], "start") == 0)
    {
        if(argc > 2)
        {
            const char *bus = argv[2];
            iray_camera_init(bus);
        }
    }

    return RT_EOK; 
}
//INIT_APP_EXPORT(rt_lvgl_demo_init); 
MSH_CMD_EXPORT_ALIAS(rt_lvgl_iray_camera_cmd, lvgl_iray,lvgl iray);

struct grey_boundary_param
{
    float a,b,c,d;
    float min,max;
    int w,h;
    int max_index;
};
static inline int grey_find_max_min(float *grey, int num, float *max, float *min)
{
    int max_index = 0;
    float tmp_max = grey[0], tmp_min = grey[0];
    for(int i=1; i<num; i++)
    {
        if(grey[i] < tmp_min)
        {
            tmp_min = grey[i];
        }
        if(grey[i] > tmp_max)
        {
            tmp_max = grey[i];
            max_index = i;
        }
    }
    if(max)
    {
        *max = tmp_max;
    }
    if(min)
    {
        *min = tmp_min;
    }
    return max_index;
}
void grey_update_boundary_param(float *grey, const int w, const int h, struct grey_boundary_param *bound)
{
    float max, min;
    bound->max_index = grey_find_max_min(grey, w*h, &max, &min);
    bound->a = min + (max - min) * 0.2121f;
    bound->b = min + (max - min) * 0.3182f;
    bound->c = min + (max - min) * 0.4242f;
    bound->d = min + (max - min) * 0.8182f;
    bound->w = w;
    bound->h = h;
    bound->min = min;
    bound->max = max;
}
#define constrain(v, min, max)      ((v) > (max)?(max):(v)<(min)?(min):(v))
/**
 * @brief grey2rgb
*/
void grey2color(float *grey_p, lv_color_t *color_p, int num, struct grey_boundary_param *bound)
{
    float v,min = bound->min,max = bound->max;
    float a = bound->a,b = bound->b,c = bound->c,d = bound->d;
    uint8_t cr,cg,cb;
    
    for(int i=0; i<num; i++)
    {
        v = grey_p[i];
        cr = constrain(255.0f / (c - b) * v - ((b * 255.0f) / (c - b)), 0, 255);
        if((v > min) & (v < a))
        {
            cg = constrain(255.0f / (a - min) * v - ((min * 255.0f) / (a - min)), 0, 255);
        }
        else if((v >= a) & (v <= c))
        {
            cg = 255;
        }
        else if(v > c)
        {
            cg = constrain(255.0f / (c - d) * v - ((d * 255.0f) / (c - d)), 0, 255);
        }
        else if((v > d) | (v < a))
        {
            cg = 0;
        }
        
        if(v <= b)
        {
            cb = constrain(255.0f / (a - b) * v - ((b * 255.0f) / (a - b)), 0, 255);
        }
        else if((v > b) & (v <= d))
        {
            cb = 0;
        }
        else if(v > d)
        {
            cg = constrain(240.0f / (max - d) * v - ((d * 240.0f) / (max - d)), 0, 240);
        }
        color_p[i] = lv_color_hex((cr << 16) | (cg << 8) | cb);
    }
    
}
void grey_resize(float *grey_p, int iw, int ih, float *grey_op, int ow, int oh)
{
    float w_scale, h_scale, pos_x, pos_y, dx, dy, grey0y, grey1y, greyxy;
    uint8_t grey00, grey01, grey10, grey11;
    int pos_xs, pos_xe, pos_ys, pos_ye;
    //int iw = bound->w;
    //int ih = bound->h;
    
    w_scale = (float)(iw - 1) / (ow - 1);
    h_scale = (float)(ih - 1) / (oh - 1);
    
    for(int x = 0; x < ow; x++)
    {
        for(int y = 0; y < oh; y++)
        {
            pos_x = x * w_scale;
            pos_y = y * h_scale;
            pos_xs = (int)pos_x;
            pos_ys = (int)pos_y;
            if(pos_xs == iw - 1)
            {
                pos_xs = iw - 2;
            }
            if(pos_ys == ih - 1)
            {
                pos_ys = ih - 2;
            }
            pos_xe = pos_xs + 1;
            pos_ye = pos_ys + 1;
            
            dx = pos_x - pos_xs;
            dy = pos_y - pos_ys;
            
            grey00 = grey_p[pos_ys * iw + pos_xs];
            grey01 = grey_p[pos_ys * iw + pos_xe];
            grey10 = grey_p[pos_ye * iw + pos_xs];
            grey11 = grey_p[pos_ye * iw + pos_xe];
            
            grey0y = grey01 * dx + (1 - dx) * grey00;
            grey1y = grey11 * dx + (1 - dx) * grey10;
            greyxy = grey1y * dy + (1 - dy) * grey0y;
            
            grey_op[y * ow + x] = greyxy;
        }
    }
}
void grey2color_resize(float *grey_p, struct grey_boundary_param *bound, lv_color_t *color_p, int ow, int oh)
{
    float w_scale, h_scale, pos_x, pos_y, dx, dy, grey0y, grey1y, greyxy;
    uint8_t grey00, grey01, grey10, grey11;
    int pos_xs, pos_xe, pos_ys, pos_ye;
    int iw = bound->w;
    int ih = bound->h;
    
    w_scale = (float)(iw - 1) / (ow - 1);
    h_scale = (float)(ih - 1) / (oh - 1);
    
    for(int x = 0; x < ow; x++)
    {
        for(int y = 0; y < oh; y++)
        {
            pos_x = x * w_scale;
            pos_y = y * h_scale;
            pos_xs = (int)pos_x;
            pos_ys = (int)pos_y;
            if(pos_xs == iw - 1)
            {
                pos_xs = iw - 2;
            }
            if(pos_ys == ih - 1)
            {
                pos_ys = ih - 2;
            }
            pos_xe = pos_xs + 1;
            pos_ye = pos_ys + 1;
            
            dx = pos_x - pos_xs;
            dy = pos_y - pos_ys;
            
            grey00 = grey_p[pos_ys * iw + pos_xs];
            grey01 = grey_p[pos_ys * iw + pos_xe];
            grey10 = grey_p[pos_ye * iw + pos_xs];
            grey11 = grey_p[pos_ye * iw + pos_xe];
            
            grey0y = grey01 * dx + (1 - dx) * grey00;
            grey1y = grey11 * dx + (1 - dx) * grey10;
            greyxy = grey1y * dy + (1 - dy) * grey0y;
            
            grey2color(&greyxy, &color_p[y * ow + x], 1, bound);
        }
    }
}
void generateLegend(lv_color_t *color_p, int w, int h, struct grey_boundary_param *bound)
{
    float temp_scale = (bound->max - bound->min) / w;
    float grey, min = bound->min;
    lv_color_t color;
    for(int i=0; i<w; i++)
    {
        grey = min + (temp_scale * i);
        grey2color(&grey, &color, 1, bound);
        for(int j = 0; j < h; j++)
        {
            color_p[(j * w) + i] = color;
        }
    }
}
void colorBarSetMaxMin(float max, float min)
{
    char str[16];
    int tmp_max_i = (int)(max*10);
    int tmp_min_i = (int)(min*10);
    rt_sprintf(str, "MAX%d.%d", tmp_max_i/10, abs(tmp_max_i%10));
    lv_label_set_text(iray_color_bar_max_value, str);
    lv_obj_align(iray_color_bar_max_value, iray_color_bar_img, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
    rt_sprintf(str, "MIN%d.%d", tmp_min_i/10, abs(tmp_min_i%10));
    lv_label_set_text(iray_color_bar_min_value, str);
}
void filterTemp(float *in, int w, int h, float *out)
{
    if (1) // mirror
    {
        for (int i = 0; i < w * h; i++)
        {
            out[i] = (out[i] + in[i]) / 2;
        }
    }
    else
    {
        for (int i = 0; i < h; i++)
            for (int j = 0; j < w; j++)
            {
                out[w * i + 31 - j] = (out[w * i + 31 - j] + in[w * i + j]) / 2;
            }
    }
}
#define _drawPoint(pixels, index, point) \
{ \
    int x = index % LV_IMG_WIDTH; \
    int y = index / LV_IMG_WIDTH; \
    rt_kprintf("x,y:%d,%d\r\n", x, y);\
    for(int i=0; i<_DRAW_POINT_TYPE; i++) \
    { \
        if(((y + i - _DRAW_POINT_TYPE_OFFSET) < 0) || ((y + i - _DRAW_POINT_TYPE_OFFSET) >= LV_IMG_HEIGHT)) \
        { \
            continue; \
        } \
        for(int j=0; j<_DRAW_POINT_TYPE; j++) \
        { \
            if(((x + j - _DRAW_POINT_TYPE_OFFSET) < 0) || ((x + j - _DRAW_POINT_TYPE_OFFSET) >= LV_IMG_WIDTH)) \
            { \
                continue; \
            } \
            if(point & (1 << (i * _DRAW_POINT_TYPE + j))) \
            { \
                pixels[(y + _DRAW_POINT_TYPE_OFFSET - i) * LV_IMG_WIDTH + (x + _DRAW_POINT_TYPE_OFFSET - j)] = BMP_PIXEL(0, 0, 0); \
            } \
        } \
    } \
}
void drawPoint3(bmp_pixel *pixels, int index, uint16_t point)
{
    #undef _DRAW_POINT_TYPE
    #undef _DRAW_POINT_TYPE_OFFSET
    #define _DRAW_POINT_TYPE            3
    #define _DRAW_POINT_TYPE_OFFSET     1
    _drawPoint(pixels, index, point);
    #undef _DRAW_POINT_TYPE
    #undef _DRAW_POINT_TYPE_OFFSET
}
void drawPoint5(bmp_pixel *pixels, int index, uint16_t point)
{
    #undef _DRAW_POINT_TYPE
    #undef _DRAW_POINT_TYPE_OFFSET
    #define _DRAW_POINT_TYPE            5
    #define _DRAW_POINT_TYPE_OFFSET     2
    _drawPoint(pixels, index, point);
    #undef _DRAW_POINT_TYPE
    #undef _DRAW_POINT_TYPE_OFFSET
}
void appendPoint(bmp_pixel *pixels, struct point_object *points, int num)
{
    for(int i=0; i<num; i++)
    {
        if(lv_obj_is_visible(points[i].lv_text))
        {
            int x = lv_obj_get_x(points[i].lv_text);
            int y = lv_obj_get_y(points[i].lv_text);
            int index = (y * LV_IMG_WIDTH) + x;
            int value = points[i].value;
            if((value & POINT_TYPE_MASK) == POINT_TYPE_3)
            {
                drawPoint3(pixels, index, value);
            }
            else
            {
                drawPoint5(pixels, index, value);
            }
        }
    }
}
void color2pixels(lv_color_t *color_buf, bmp_pixel *pixels, int w, int h)
{
    lv_color_t color;
    for(int i=0; i<w*h; i++)
    {
        color = color_buf[i];
        pixels[i] = BMP_PIXEL(LV_COLOR_GET_R(color)<<3,LV_COLOR_GET_G(color)<<2,LV_COLOR_GET_B(color)<<3);
    }
}
void pixels2color(bmp_pixel *pixels, lv_color_t *color_buf, int w, int h)
{
    for(int i=0; i<w*h; i++)
    {
        color_buf[i]  = LV_COLOR_MAKE(pixels[i].red, pixels[i].green, pixels[i].blue);
    }
}
/**
 *
*/
static int _mlx90640_i2cread(struct rt_i2c_bus_device *bus, uint8_t slave_addr, uint16_t start_addr, uint16_t num, uint16_t *data)
{
    uint8_t *bp;
    rt_ssize_t ret;
    uint8_t start[2];
    struct rt_i2c_msg msg[2];
#if 1
    bp = (uint8_t *)data;
    start[0] = (start_addr) >> 8;
    start[1] = (start_addr) & 0x00FF;
    
    msg[0].addr = slave_addr;
    msg[0].buf = start;
    msg[0].len = 2;
    msg[0].flags = RT_I2C_WR|RT_I2C_NO_STOP;
    msg[1].addr = slave_addr;
    msg[1].buf = bp;
    msg[1].len = num*sizeof(uint16_t);
    msg[1].flags = RT_I2C_RD;

    ret = rt_i2c_transfer(bus, msg, 2);
    if (ret != 2)
    {
        return -1;
    }

    for(int cnt=0; cnt < num*sizeof(uint16_t); cnt+=2) {
        uint8_t tmpbytelsb = bp[cnt+1];
        bp[cnt+1] = bp[cnt];
        bp[cnt] = tmpbytelsb;
    }
#else
    uint16_t trans_size;
    for(uint16_t off = 0; off < num;)
    {
        trans_size = num - off;
        if(trans_size > 0x10)
        {
            trans_size = 0x10;
        }
        bp = (uint8_t *)&data[off];
        start[0] = (start_addr + off) >> 8;
        start[1] = (start_addr + off) & 0x00FF;
        
        msg[0].addr = slave_addr;
        msg[0].buf = start;
        msg[0].len = 2;
        msg[0].flags = RT_I2C_WR|RT_I2C_NO_STOP;
        msg[1].addr = slave_addr;
        msg[1].buf = bp;
        msg[1].len = trans_size*sizeof(uint16_t);
        msg[1].flags = RT_I2C_RD;

        ret = rt_i2c_transfer(bus, msg, 2);
        if (ret != 2)
        {
            return -1;
        }

        for(int cnt=0; cnt < trans_size*sizeof(uint16_t); cnt+=2) {
            uint8_t tmpbytelsb = bp[cnt+1];
            bp[cnt+1] = bp[cnt];
            bp[cnt] = tmpbytelsb;
        }
        off += trans_size;
    }
#endif
    return 0;   
} 

int _mlx90640_i2cwrite(struct rt_i2c_bus_device *bus, uint8_t slave_addr, uint16_t write_addr, uint16_t data)
{
    rt_ssize_t ret;
    uint8_t buf[4];
    uint16_t data_check;
    struct rt_i2c_msg msg[1];

    buf[0] = write_addr >> 8;
    buf[1] = write_addr & 0x00FF;
    buf[2] = data >> 8;
    buf[3] = data & 0x00FF;

    msg[0].addr = slave_addr;
    msg[0].buf = buf;
    msg[0].len = 4;
    msg[0].flags = RT_I2C_WR;

    ret = rt_i2c_transfer(bus, msg, 1);
    if (ret != 1)
    {
        return -1;
    }

    _mlx90640_i2cread(bus, slave_addr, write_addr, 1, &data_check);

    if ( data_check != data)
    {
        return -2;
    }    

    return 0;
}
int MLX90640_I2CRead(uint8_t slave_addr, uint16_t start_addr, uint16_t num, uint16_t *data)
{
    if(i2c_bus == NULL)
    {
        return -1;
    }
    return _mlx90640_i2cread(i2c_bus, slave_addr, start_addr, num, data);
}
int MLX90640_I2CWrite(uint8_t slave_addr, uint16_t write_addr, uint16_t data)
{
    if(i2c_bus == NULL)
    {
        return -1;
    }
    return _mlx90640_i2cwrite(i2c_bus, slave_addr, write_addr, data);
}

#include "MLX90640_API.h"

#define  FPS2HZ   0x02
#define  FPS4HZ   0x03
#define  FPS8HZ   0x04
#define  FPS16HZ  0x05
#define  FPS32HZ  0x06

#define MLX90640_ADDR       0x33
#define RefreshRate         FPS8HZ
#define EMMISIVITY          0.95f
#define TA_SHIFT            8 //Default shift for MLX90640 in open air

#define IRAY_IMG_WIDTH          32
#define IRAY_IMG_HEIGHT         24
static paramsMLX90640 mlx90640;
//static float readValues[IRAY_IMG_WIDTH*IRAY_IMG_HEIGHT];
static float tempValues[IRAY_IMG_WIDTH*IRAY_IMG_HEIGHT];
static float tempResizeValues[LV_IMG_WIDTH*LV_IMG_HEIGHT];
static uint16_t mlx90640Frame[834];
static struct grey_boundary_param mlx90640Bound;
void grey_boundary_param_print(void)
{
    int max_index = mlx90640Bound.max_index;
    int w = mlx90640Bound.w;
    int h = mlx90640Bound.h;
    int x = max_index % LV_IMG_WIDTH;
    int y = max_index / LV_IMG_WIDTH;
    rt_kprintf("max_index:%d, (%d,%d)/(%d,%d), %dx%d\r\n"
        , max_index
        , x, y, lv_obj_get_x(max_point.lv_text), lv_obj_get_y(max_point.lv_text)
        , w, h);
}
// Read pixel data from MLX90640.
int readTempValues(void)
{
    for (uint8_t x = 0 ; x < 2 ; x++) // Read both subpages
    {
        //uint16_t mlx90640Frame[834];
        int status = MLX90640_GetFrameData(MLX90640_ADDR, mlx90640Frame);
        if (status < 0)
        {
            //rt_kprintf("GetFrame Error: %d, %d\r\n", x, status);
            return -1;
        }

        float vdd = MLX90640_GetVdd(mlx90640Frame, &mlx90640);
        float Ta = MLX90640_GetTa(mlx90640Frame, &mlx90640);

        float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature

        MLX90640_CalculateTo(mlx90640Frame, &mlx90640, EMMISIVITY, tr, tempValues);
        MLX90640_BadPixelsCorrection(mlx90640.brokenPixels, tempValues, 1, &mlx90640);
    }
    return 0;
}
void generatePicture(void)
{
    //
    //filterTemp(readValues, IRAY_IMG_WIDTH, IRAY_IMG_HEIGHT, tempValues);
    //
    //grey_update_boundary_param(tempValues, IRAY_IMG_WIDTH, IRAY_IMG_HEIGHT, &mlx90640Bound);
    //grey2color_resize(tempValues, &mlx90640Bound, img_buf, LV_IMG_WIDTH, LV_IMG_HEIGHT);
    grey_resize(tempValues, IRAY_IMG_WIDTH, IRAY_IMG_HEIGHT, tempResizeValues, LV_IMG_WIDTH, LV_IMG_HEIGHT);
    grey_update_boundary_param(tempResizeValues, LV_IMG_WIDTH, LV_IMG_HEIGHT, &mlx90640Bound);
    grey2color(tempResizeValues, img_buf, (LV_IMG_WIDTH * LV_IMG_HEIGHT), &mlx90640Bound);
    //drawLegend(, struct grey_boundary_param *bound);
    generateLegend(color_bar_buf, LV_COLOR_BAR_WIDTH, LV_COLOR_BAR_HEIGHT, &mlx90640Bound);
    colorBarSetMaxMin(mlx90640Bound.max, mlx90640Bound.min);
}
void updatePointsAndMesg(void)
{
    updateMaxPoint(mlx90640Bound.max_index, LV_IMG_WIDTH);
    updateMaxPointMesg(tempResizeValues, false);
    updatePointsMesg(tempResizeValues, true);
}
void tempSensorInit(void)
{
    uint16_t *eeMLX90640 = mlx90640Frame;
    int status = MLX90640_DumpEE(MLX90640_ADDR, eeMLX90640);
    if (status != 0) rt_kprintf("\r\nload system parameters error with code:%d\r\n",status);
    status = MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
    if (status != 0) rt_kprintf("\r\nParameter extraction failed with error code:%d\r\n",status);
    MLX90640_SetRefreshRate(MLX90640_ADDR, RefreshRate);
    //MLX90640_SetChessMode(MLX90640_ADDR);
}
// file system
#include "fcntl.h"
#include "sys/unistd.h"
int libbmp_file_write(void *file, int offset, void *buf, int size)
{
    return write((int)file, buf, size);
}
int libbmp_file_read(void *file, int offset, void *buf, int size)
{
    return read((int)file, buf, size);
}
int libbmp_file_seek(void *file, int offset, int pos)
{
    return lseek((int)file, offset, pos);
}
